#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/iterable/stl_interop.h>
#include <vex.tests/mstest/tests.range/tests.range.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests {	namespace range_stl_iterator_from_range {

    using namespace boost::assign;

    namespace vi = vex::iterable;

    TEST_CLASS(range_stl_iterator_from_range)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.range.stl.iterator_from_range")
        END_TEST_CLASS_ATTRIBUTE();

        static const std::size_t m_container_size = 4u;
        typedef std::vector<int> container_t;
        typedef container_t::iterator iterator_t;
        typedef container_t::const_iterator const_iterator_t;

        container_t 
            m_container, 
            m_container_same_size_different_values, 
            m_container_empty, 
            m_container_with_one_element;
    public:
        TEST_METHOD_INITIALIZE(set_up)
        {
            container_t().swap(m_container);
            container_t().swap(m_container_same_size_different_values);
            container_t().swap(m_container_empty);
            container_t().swap(m_container_with_one_element);

            m_container += 1, 2, 3, 4, 5;
            m_container_same_size_different_values += 6, 7, 8, 9, 5;
            m_container_with_one_element += 1;
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
            container_t().swap(m_container);
            container_t().swap(m_container_same_size_different_values);
            container_t().swap(m_container_empty);
            container_t().swap(m_container_with_one_element);
        }

        TEST_METHOD(concepts_and_traits_are_satisfied)
        {
            typedef decltype(vi::make_range(m_container)) range_t;
            typedef vi::range_traits<range_t> traits_t;
            typedef decltype(vi::make_stl_iterator_range(std::declval<range_t&>())) iterator_range_t;
            typedef iterator_range_t::iterator iterator_t;
            typedef std::iterator_traits<iterator_t> iterator_traits_t;

            BOOST_CONCEPT_ASSERT((vi::concept::RandomAccessRange<range_t>));
            BOOST_CONCEPT_ASSERT((boost::SinglePassRangeConcept<iterator_range_t>));
            BOOST_CONCEPT_ASSERT((boost::InputIterator<iterator_t>));

            Assert::IsTrue(std::is_same<container_t::reference, traits_t::reference>::value);
            Assert::IsTrue(std::is_same<container_t::value_type, traits_t::value_type>::value);
            Assert::IsTrue(std::is_same<container_t::pointer, traits_t::pointer>::value);

            Assert::IsTrue(std::is_same<traits_t::reference, iterator_traits_t::reference>::value);
            Assert::IsTrue(std::is_same<traits_t::value_type, iterator_traits_t::value_type>::value);
            Assert::IsTrue(std::is_same<traits_t::pointer, iterator_traits_t::pointer>::value);
        }

        TEST_METHOD(make_stl_iterator_range_adapts_range_for_stl_usage)
        {
                VEX_TESTS_SCOPED_MEM_CHECK();
                auto r = vi::make_range(m_container);
                container_t t_target;
                for (auto const & v : vi::make_stl_iterator_range(r)) {
                    t_target.push_back(v);
                }
                // iterable is consumed:
                Assert::IsFalse(r.move_front());
                one_pass_range::are_equivalent(
                    vi::make_range(t_target), vi::make_range(m_container));
        }
        
        TEST_METHOD(make_stl_iterator_range_adapts_range_with_one_element_for_stl_usage)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            auto r = vi::make_range(m_container_with_one_element);
            container_t t_target;
            for (auto const & v : vi::make_stl_iterator_range(r)) {
                t_target.push_back(v);
            }
            // iterable is consumed:
            Assert::IsFalse(r.move_front());
            one_pass_range::are_equivalent(
                vi::make_range(t_target), vi::make_range(m_container_with_one_element));
        }

        TEST_METHOD(make_stl_iterator_range_adapts_empty_range_for_stl_usage)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            {
                auto r = vi::make_range(m_container_empty);
                container_t t_target;
                for (auto const & v : vi::make_stl_iterator_range(r)) {
                    t_target.push_back(v);
                }
                // iterable is consumed:
                Assert::IsFalse(r.move_front());
                one_pass_range::are_equivalent(
                    vi::make_range(t_target), vi::make_range(m_container_empty));
            }
        }

        TEST_METHOD(make_stl_iterator_range_allows_reading_from_range_in_range_based_for_loop)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            auto r = vi::make_range(m_container);

            container_t t_target;
            for (auto const & v : vi::make_stl_iterator_range(r)) {
                t_target.push_back(v);
            }

            one_pass_range::are_equivalent(vi::make_range(t_target), vi::make_range(m_container));
        }

        TEST_METHOD(make_stl_iterator_range_allows_writing_to_range_in_range_based_for_loop)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            auto r = vi::make_range(m_container);

            container_t t_exp = m_container;
            for (auto & v : t_exp) v += 1;

            for (auto & v : vi::make_stl_iterator_range(r)) {
                v += 1;
            }

            one_pass_range::are_equivalent(vi::make_range(t_exp), vi::make_range(m_container));
        }
    };
}}